package main;

import java.util.regex.*;
import java.util.*;
import java.io.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.Pattern.*;
import main.DataTransferObject;
import dao.AccessesDAO;
import dao.AccessesDAOImpl;
import service.LocationService;
import service.TarService;
import java.text.SimpleDateFormat;
import java.text.ParseException;
import java.util.zip.GZIPInputStream;

import org.apache.tools.tar.TarEntry;
import org.apache.tools.tar.TarInputStream;

/**
 * Process log files to add be added to server.
 *
 * can process string coming from server,
 * process log file or even tar files with multiples files.
 *
 * @author      Rebeen Hawramy
 *
 */
public class logParser{
    //create all the patterns i need right away here.
    // user will work for no user and user.
    private final Pattern USER_CONNECTS = Pattern.compile( "[0-9]{1,2}/[0-9]{1,2}/[0-9]{2}\\s[0-9]{1,2}:[0-9]{1,2}\\s[A,P]M - INFO: Accepted connection [0-9]+ from (([2]([0-4][0-9]|[5][0-5])|[0-1]?[0-9]?[0-9])[.]){3}(([2]([0-4][0-9]|[5][0-5])|[0-1]?[0-9]?[0-9]))");
    //first two are used for retrieving username and last two are used for algname.
    private final Pattern STRING1_OF_ALG = Pattern.compile( "[0-9]{1,2}/[0-9]{1,2}/[0-9]{2}\\s[0-9]{1,2}:[0-9]{1,2}\\s[A,P]M[a-z,A-Z,:,.,\\-,\\ ]+[/a-z,_]+[0-9]+\\s[*]");
    private final Pattern STRING2_OF_ALG = Pattern.compile( "[0-9]{1,2}/[0-9]{1,2}/[0-9]{2}\\s[0-9]{1,2}:[0-9]{1,2}\\s[A,P]M - FINE: Up front with user  [a-zA-Z, @,. ,_]+ looking for -- [a-zA-Z0-9,//,_,.,*,-]+");
    private final Pattern STRING3_OF_ALG = Pattern.compile( "[0-9]{1,2}/[0-9]{1,2}/[0-9]{2}\\s[0-9]{1,2}:[0-9]{1,2}\\s[A,P]M - FINE: http://jhave.org/jhave/html_root/uid/[0-9]+.[a-zA-Z]+");
    private final Pattern STRING4_OF_ALG = Pattern.compile( "[0-9]{1,2}/[0-9]{1,2}/[0-9]{2}\\s[0-9,:]+\\s[A-Z]{2} - FINE: sendtoClient - 400 (\\S+(?:/\\S+)?) .*");
    private final Pattern KILL_UID_CLIENT = Pattern.compile( "\\d{1,2}/[0-9]{1,2}/[0-9]{2}\\s[0-9]{1,2}:[0-9]{1,2}\\s[A,P]M - FINE: Killing client [0-9]+");
    private final Pattern NO_USER_EMAIL = Pattern.compile("[0-9]{1,2}/[0-9]{1,2}/[0-9]{2}\\s[0-9]{1,2}:[0-9]{1,2}\\s[A,P]M - FINE: up front looking for -- [a-z,A-Z, / ]+");
    private AccessesDAO accessDAO;
    private ArrayList<DataTransferObject> dtoArray;
    private int lastid;
    private int lastidalgname;

    public logParser(){
        accessDAO = new AccessesDAOImpl();
        dtoArray = new ArrayList<DataTransferObject>();
    }

    /**
    * Log line parser
    *
    * This function takes in a log line and parses out
    * the values needed from that line.
    *
    * @param  String inpustr to parse
    * @return void
    */
    public void stringParse(String inputstr)
    {
        Matcher m1 =  USER_CONNECTS.matcher(inputstr.subSequence(0,inputstr.length()));
        boolean bm1 = m1.matches();

        Matcher m2 =  STRING1_OF_ALG.matcher(inputstr.subSequence(0,inputstr.length()));
        boolean bm2 = m2.matches();

        Matcher m3 =  STRING2_OF_ALG.matcher(inputstr.subSequence(0,inputstr.length()));
        boolean bm3 = m3.matches();

        Matcher m4 =  KILL_UID_CLIENT.matcher(inputstr.subSequence(0,inputstr.length()));
        boolean bm4 = m4.matches();

        Matcher m5 =  NO_USER_EMAIL.matcher(inputstr.subSequence(0,inputstr.length()));
        boolean bm5 = m5.matches();

        Matcher m6 =  STRING3_OF_ALG.matcher(inputstr.subSequence(0,inputstr.length()));
        boolean bm6 = m6.matches();

        Matcher m7 =  STRING4_OF_ALG.matcher(inputstr.subSequence(0,inputstr.length()));
        boolean bm7 = m7.matches();

        if(bm1)
        {
            //System.out.println("login line");
            //make a new patterns for Date,UID,IP and split those from string.
            Pattern date = Pattern.compile("[0-9]{1,2}/[0-9]{1,2}/[0-9]{2}\\s[0-9,:]+\\s[A-Z]{2}");
            Matcher mdate = date.matcher(inputstr);
            mdate.find();
            Pattern uid = Pattern.compile("\\s[0-9]+\\s"); 
            Matcher muid = uid.matcher(inputstr);
            muid.find();
            Pattern ip = Pattern.compile("(([2]([0-4][0-9]|[5][0-5])|[0-1]?[0-9]?[0-9])[.]){3}(([2]([0-4][0-9]|[5][0-5])|[0-1]?[0-9]?[0-9]))");
            Matcher mip = ip.matcher(inputstr);
            mip.find();

            // Split phrase into words
            String datestr = mdate.group(0);
            SimpleDateFormat formatter;
            Date ndate;
            Calendar datetoCal = Calendar.getInstance();

            try{
                formatter = new SimpleDateFormat("M/dd/yy HH:mm a"); //please notice the capital M
                ndate = formatter.parse(datestr);
                datetoCal.setTime(ndate);
            }catch(ParseException e)
            {
                e.printStackTrace();
            }
            String uidstr = muid.group(0);
            uidstr = uidstr.trim();
            int intuid = Integer.parseInt(uidstr);
            String ipstr = mip.group(0);

            //make call to Brads server.
            DataTransferObject user = new DataTransferObject();
            user.setUserID(intuid);
            user.setTime(datetoCal);
            user.setIp(ipstr);

            dtoArray.add(user);
        }else if(bm4)
        {
            //make a new patterns for UID and split those from string.
            Pattern uid = Pattern.compile("client\\s[0-9]+");
            Matcher muid = uid.matcher(inputstr);
            muid.find();

            // Split phrase into words
            String uidstr = muid.group(0);
            uidstr = uidstr.substring(6,uidstr.length());
            uidstr = uidstr.trim();
            int intuid = Integer.parseInt(uidstr);

            int removeposition = this.positionofuid(intuid);

            if(removeposition != 9999)
                dtoArray.remove(removeposition);
        }else if(bm5)
        {

            //make a new patterns for empty user shouldnt need split except for ALG.
            Pattern date = Pattern.compile("[0-9]{1,2}/[0-9]{1,2}/[0-9]{2}\\s[0-9,:]+\\s[A-Z]{2}");
            Matcher mdate = date.matcher(inputstr);
            mdate.find();
//             Pattern algname = Pattern.compile("\\s[a-z,A-Z]{3}/[a-z,A-Z]+"); //need to get substring of String "[space]exe/" +algname
//             Matcher malg = algname.matcher(inputstr);
//             malg.find();

            // Split phrase into words
            String nouser = "nouser";
//             String algstr = malg.group(0);
            String datestr = mdate.group(0);
            SimpleDateFormat formatter;
            Date ndate;
            Calendar datetoCal = Calendar.getInstance();

            try{
                formatter = new SimpleDateFormat("M/dd/yy HH:mm a"); //please notice the capital M
                ndate = formatter.parse(datestr);
                datetoCal.setTime(ndate);
            }catch(ParseException e)
            {
                e.printStackTrace();
            }

            //find dto for nouser in dtoarray.
            //make call to Brads server.
            DataTransferObject user = null;//this.find(nouser);  //this might cause an issue if nouser dta object is there.
            if(this.find(nouser) == null)
            {
                int positionofuser = positionofuid(lastid);
                if(positionofuser != 9999)
                {
                    user = dtoArray.get(positionofuser);
                    user.setUserName(nouser);
                    user.setTime(datetoCal);
                }else
                {
                    System.out.println("SomeThing is wrong NOOB");
                }
            }else{
            	user = this.find(nouser);
                user.setTime(datetoCal);
            }

//             user.setAlgorithmName(algstr);
//             accessDAO.createEntry(user);
        }else if(bm2)
        {
            //make a new patterns for UID and split those from string.
            Pattern uid = Pattern.compile("[0-9]+\\s[*]");
            Matcher muid = uid.matcher(inputstr);
            muid.find();

            // Split phrase into words
            String uidstr = muid.group(0);
            uidstr = uidstr.substring(0,uidstr.length()-1);
            uidstr = uidstr.trim();

            //lastid set to use to retrieve emailname
            lastid = Integer.parseInt(uidstr);
        }else if(bm3)
        {
            //make a new patterns for Username,AlgName and split those from string.
            Pattern date = Pattern.compile("[0-9]{1,2}/[0-9]{1,2}/[0-9]{2}\\s[0-9,:]+\\s[A-Z]{2}");
            Matcher mdate = date.matcher(inputstr);
            mdate.find();
            Pattern email = Pattern.compile("user  [a-zA-Z0-9,@,.,_]+ looking"); //need to get substring of String "[space]exe/" +algname
            Matcher memail = email.matcher(inputstr);
            memail.find();
//             Pattern algname = Pattern.compile("\\s[a-z,A-Z]{3}/[a-z,A-Z]+"); //need to get substring of String "[space]exe/" +algname
//             Matcher malg = algname.matcher(inputstr);
//             malg.find();

            // Split phrase into words
            String emailstr = memail.group(0);
            emailstr = emailstr.substring(6,emailstr.length()-7);

//             String algstr = malg.group(0);
//             algstr = algstr.substring(5,algstr.length());
            String datestr = mdate.group(0);
            SimpleDateFormat formatter;
            Date ndate;
            Calendar datetoCal = Calendar.getInstance();

            try{
                formatter = new SimpleDateFormat("M/dd/yy HH:mm a"); //please notice the capital M
                ndate = formatter.parse(datestr);
                datetoCal.setTime(ndate);
            }catch(ParseException e)
            {
                e.printStackTrace();
            }

            //make call to Brads server.
            DataTransferObject user = null;
            if(this.find(emailstr) == null)
            {
                int positionofuser = positionofuid(lastid);
                if(positionofuser != 9999)
                {
                    user = dtoArray.get(positionofuser);
                    user.setUserName(emailstr);
                }else
                {
                    System.out.println("SomeThing is wrong NOOB");
                }
            }else{
            	user = this.find(emailstr);
            }

            user.setTime(datetoCal);
        }else if(bm6)
        {
            //make a new patterns for UID and split those from string.
            Pattern uid = Pattern.compile("/[0-9]+.[a-zA-Z]");
            Matcher muid = uid.matcher(inputstr);
            muid.find();

            // Split phrase into words
            String uidstr = muid.group(0);
            uidstr = uidstr.substring(1,uidstr.length()-2);

            lastidalgname = Integer.parseInt(uidstr);
        }else if(bm7)
        {
            //make a new patterns for Username,AlgName and split those from string.
            Pattern algname = Pattern.compile("400 [a-zA-Z0-9,/,.,_]+ ");
            Matcher malg = algname.matcher(inputstr);
            malg.find();

            // Split phrase into words
            String algstr = malg.group(0);
            algstr = algstr.substring(4,algstr.length());

            //make call to Brads server.
            DataTransferObject user = null;
            int positionofuser = positionofuid(lastidalgname);
            if(positionofuser != 9999)
            {
                user = dtoArray.get(positionofuser);
                user.setAlgorithmName(algstr);

                if(user.getUserName() != null)
                    accessDAO.createEntry(user);
            }
        }
    }

    /**
    * find doaobject of specific user in array.
    *
    * Trace through array and return the doauser object
    * for the username if there is one in array.
    *
    * @param  username the usename that exists in user arraylist.
    * @return DataTransferObject
    */
    public DataTransferObject find(String username)
    {
        for(DataTransferObject dta:dtoArray)
        {
        	if(dta.getUserName() != null)
	            if(dta.getUserName().equals(username))
	            {
	                return dta;
	            }
        }

        return null;
    }

    /**
    * Find user in Arraylist of users.
    *
    * find the position of users uid in array list
    * and return the position other wise return 9999.
    *
    * @param  uid User uid i want to find in arraylist to return his/her position
    * @return int position of user in arraylist.
    */
    public int positionofuid(int uid)
    {
        int counter = 0;
        for(DataTransferObject dta:dtoArray)
        {
            if(dta.getUserID() == uid)
            {
                return counter;
            }
            counter++;
        }

        return 9999;
    }

    /**
    * File parser reads in a file and parses each line.
    *
    * parse a file with stringparser function.
    *
    * @param  rfile file to read and parse. 
    * @return void
    */
    public void fileParse(File rfile)
    {
        try {
            /*  Sets up a file reader to read the file passed on the command
            line one character at a time */
            //create a file from name
            /* Filter FileReader through a Buffered read to read a line at a time */
            BufferedReader bufRead = new BufferedReader(new FileReader(rfile));

            String line;    // String that holds current file line

            // Read first line
            line = bufRead.readLine();

            // Read through file one line at time. Print line # and line
            while (line != null){
                line = bufRead.readLine();
                if(line == null)
                    break;
                stringParse(line);
            }

            bufRead.close();
        }catch (ArrayIndexOutOfBoundsException e){
            /* If no file was passed on the command line, this expception is
            generated. A message indicating how to the class should be
            called is displayed */
            System.out.println("Usage: java ReadFile filename\n");

        }catch (Exception e){
            // If another exception is generated, print a stack trace
            e.printStackTrace();
        }
    }

    /**
    * Zip file parser
    *
    * takes in a filename of zip file and is untared with TarService
    * then we recieve all the files from TarService then are sorted to be
    * then parsed by fileparser function. 
    *
    * @param  filename name of zipfile to parse.
    * @return void.
    */
    public void zipParse(String filename)
    {    	
        try {
        	//Extracts to temp directory...
			TarService.unTarGZ(filename);
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		//Use extracted temp directory.
        File tempDir = new File(TarService.TEMP_UNTAR_LOCATION);

        //Get all log files
        File[] logs = tempDir.listFiles();

        //Sort them
        Arrays.sort(logs, new FileComparator());

        for(File indexfile: logs)
        {
            this.fileParse(indexfile);
        }

        //Clean up extracted temp files
        TarService.cleanUpTemp();
    }
}